
package com.rt.schedulenew.utils.netty.service;

import com.rt.schedulenew.utils.netty.common.SocketIOProperties;
import com.rt.schedulenew.utils.util.DateUtil;
import com.rt.schedulenew.utils.util.JsonUtil;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.text.SimpleDateFormat;
import com.corundumstudio.socketio.SocketIOClient;
import com.rt.schedulenew.utils.netty.common.NettyConstants;
import javax.annotation.PreDestroy;
import javax.annotation.PostConstruct;
import com.corundumstudio.socketio.SocketIOServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.springframework.util.CollectionUtils;


/**
 * netty 底层封装
 */
public class NettySocketIOService {
    private Logger log = LoggerFactory.getLogger(NettySocketIOService.class);
    // socket 参数配置
    @Autowired
    private SocketIOProperties properties;

    @Autowired(required = false)
    private INettySocketCallBackService socketCallBackService;

    private SocketIOServer socketIOServer;

    public NettySocketIOService(SocketIOServer sioServer) {
        socketIOServer = sioServer;
    }

    @PostConstruct
    private void autoStartup() throws Exception {
        start();
    }

    @PreDestroy
    private void autoStop() throws Exception {
        stop();
    }

    /**
     * socket 启动
     */
    public void start() {
        // 客户端连接操作
        socketIOServer.addConnectListener(client -> {
            log.debug("socket连接：time: {},remoteAddress: {},sessionId: {}", DateUtil.formatDate(new Date(), DateUtil.FORMAT_DATETIME),
                    client.getRemoteAddress().toString(), client.getSessionId().toString());


        });

        // 客户端断开操作
        socketIOServer.addDisconnectListener(client -> {
            String clientId = client.getRemoteAddress().toString();
            String param = NettyConstants.sessionKeyMap.get(clientId);
            log.info("socket失去连接：time: {},remoteAddress: {},sessionId: {},param: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), client.getRemoteAddress().toString(), client.getSessionId().toString(), param);
            if (param == null) {
                return;
            }
            Map<String, String> map = NettyConstants.WebSocketClients.getParams(param);
            if (!CollectionUtils.isEmpty(map)) {
                try {
                    String login = map.get(NettyConstants.WebSocketConnectParam.Param.USER_LOGIN);
                    NettyConstants.sessionKeyMap.remove(clientId);
                    String userKey = NettyConstants.WebSocketClients.getDefaultKey(login);
                    NettyConstants.WebSocketClients.remove(userKey);
                    List<String> list = NettyConstants.userSessionIdKeyMap.get(userKey);
                    if (list != null && list.size() > 0) {
                        list.remove(clientId);
                    }
                    if (list == null || list.size() == 0) {
                        NettyConstants.userSessionIdKeyMap.remove(userKey);
                    }
                    Map<String, String> m = NettyConstants.WebSocketClients.getUserInfo(login);
                    String userId = m.get("userId");
                    m.remove("clientTypeName");
                    Map<String, SocketIOClient> clientMap2 = NettyConstants.clientMap.get(userId);
                    if (clientMap2 == null || clientMap2.size() == 0) {
                        NettyConstants.clientMap.remove(userId);
                    }
                    sendMessageToAllUsers(10, JsonUtil.toJson(m));
                    if (socketCallBackService != null) {
                        socketCallBackService.disconnectCallback(userId);
                        socketCallBackService.disconnectCallback(userId, login);
                    }
                    client.disconnect();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        // 事件监听
        socketIOServer.addEventListener(properties.getPushEvent(), NettyConstants.WebSocketAction.WebSocketActionResult.class, (client, data, ackRequest) -> {
            if (1 == data.getCode()) {
                // 获取客户端IP
                String clientId = client.getRemoteAddress().toString();
                log.info("发送消息：time: {},remoteAddress: {},sessionId: {},message: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), client.getRemoteAddress().toString(), client.getSessionId().toString(), JsonUtil.toJson((Object) data));

                Map<String, String> map = NettyConstants.WebSocketClients.getParams(data.getData());
                if (!CollectionUtils.isEmpty(map)) {
                    try {
                        String login = map.get(NettyConstants.WebSocketConnectParam.Param.USER_LOGIN);
                        String userKey = NettyConstants.WebSocketClients.getDefaultKey(login);
                        NettyConstants.WebSocketClients.add(userKey, client);
                        List<String> list = NettyConstants.userSessionIdKeyMap.get(userKey);
                        if (list != null && list.size() > 0) {
                            for (int m = 0; m < list.size(); ++m) {
                                String tmpSessionId = list.get(m);
                                NettyConstants.sessionKeyMap.remove(tmpSessionId);
                            }
                        } else {
                            list = new ArrayList<>();
                        }
                        NettyConstants.sessionKeyMap.put(clientId, data.getData());
                        list.clear();
                        list.add(clientId);
                        NettyConstants.userSessionIdKeyMap.put(userKey, list);
                        Map<String, String> i = NettyConstants.WebSocketClients.getUserInfo(login);
                        i.remove("clientTypeName");
                        sendMessageToAllUsers(1, JsonUtil.toJson(i));
                        if (socketCallBackService != null) {
                            socketCallBackService.connectCallback(i.get("userId"));
                            socketCallBackService.connectCallback(i.get("userId"), login);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        socketIOServer.start();
    }

    public void stop() {
        if (socketIOServer != null) {
            socketIOServer.stop();
            socketIOServer = null;
        }
    }

    private void sendEventMessage(String userKey, int code, String message) {
        if (userKey.startsWith(NettyConstants.ClientType.IOS.key) || userKey.startsWith(NettyConstants.ClientType.C.key)) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("code", code);
            map.put("data", JsonUtil.toJsonObj(message));
            message = JsonUtil.toJson(map);
        } else {
            message = NettyConstants.WebSocketAction.getFormatWebSocketResult(code, message);
        }
        SocketIOClient client = NettyConstants.WebSocketClients.get(userKey);
        try {
            if (client != null) {
                client.sendEvent(properties.getPushEvent(), message);
            }
        } catch (Exception e) {
            log.error("websocket sendMessage exception: key={}", userKey);
            log.error(e.getMessage(), e);
            NettyConstants.WebSocketClients.remove(userKey);
        }
    }

    private void sendEventMessageByUserId(String userId, int code, String message, String sourceType) {
        Map<String, SocketIOClient> param = NettyConstants.clientMap.get(userId);
        for (String key : param.keySet()) {
            if (key.equals(sourceType)) {
                continue;
            }
            String sendMessage = message;
            if (NettyConstants.ClientType.IOS.key.startsWith(key) || NettyConstants.ClientType.C.key.startsWith(key)) {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("code", code);
                map.put("data", JsonUtil.toJsonObj(sendMessage));
                sendMessage = JsonUtil.toJson((Object) map);
            } else {
                sendMessage = NettyConstants.WebSocketAction.getFormatWebSocketResult(code, sendMessage);
            }
            SocketIOClient client = param.get(key);
            try {
                if (client == null) {
                    continue;
                }
                client.sendEvent(properties.getPushEvent(), sendMessage);
            } catch (Exception e) {
                log.error("websocket sendMessage exception: key={}", userId);
                log.error(e.getMessage(), e);
                param.remove(key);
            }
        }
    }

    public void sendMessageToUser(String userId, int code, String message) throws Exception {
        if (!NettyConstants.WebSocketClients.hasConnection(userId)) {
            log.error(userId + " connection does not exist");
            return;
        }
        sendEventMessageByUserId(userId, code, message, "");
    }

    public void sendMessageToUserKey(String userKey, int code, String message) throws Exception {
        if (!NettyConstants.WebSocketClients.hasConnectionByKey(userKey)) {
            log.error(userKey + " connection does not exist");
            return;
        }
        sendEventMessage(userKey, code, message);
    }

    public void syncMessage(String userId, int code, String message, String sourceType) throws Exception {
        if (!NettyConstants.WebSocketClients.hasConnection(userId)) {
            return;
        }
        sendEventMessageByUserId(userId, code, message, sourceType);
    }

    public void sendMessageToAllUsers(int code, String message) throws Exception {
        Map<String, Object> userMap = new HashMap<String, Object>();
        for (String key : NettyConstants.clientMap.keySet()) {
            Map<String, SocketIOClient> param = NettyConstants.clientMap.get(key);
            if (1 == code) {
                userMap.put(key, param.size());
            }
            for (String kk : param.keySet()) {
                String sendMessage = message;
                if (NettyConstants.ClientType.IOS.key.startsWith(key) || NettyConstants.ClientType.C.key.startsWith(kk)) {
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("code", code);
                    map.put("data", JsonUtil.toJsonObj(sendMessage));
                    sendMessage = JsonUtil.toJson((Object) map);
                } else {
                    sendMessage = NettyConstants.WebSocketAction.getFormatWebSocketResult(code, sendMessage);
                }
                SocketIOClient client = param.get(kk);
                try {
                    if (client == null) {
                        continue;
                    }
                    client.sendEvent(properties.getPushEvent(), sendMessage);
                } catch (Exception e) {
                    log.error("websocket sendMessage exception: key={}", (Object) key);
                    log.error(e.getMessage(), e);
                }
            }
        }
        if (userMap != null && userMap.size() > 0) {
            log.info("当前用户数:{}，用户id-客户端数:{}", NettyConstants.clientMap.size(), userMap);
        }
    }

    public String getDefaultKey(String userKey) {
        return NettyConstants.WebSocketClients.getDefaultKey(userKey);
    }

    public boolean hasConnection(String userId) {
        return NettyConstants.WebSocketClients.hasConnection(userId);
    }

    public boolean hasConnectionByKey(String userKey) {
        return NettyConstants.WebSocketClients.hasConnectionByKey(userKey);
    }

    public SocketIOServer getSocketIOServer() {
        return socketIOServer;
    }

    public void setSocketIOServer(SocketIOServer socketIOServer) {
        socketIOServer = socketIOServer;
    }

}
